/*
 * Copyright (c) 2007 Compl Yue Still, All Rights Reserved.
 */

package hbi.wire.as3.host
{
	import flash.utils.IDataInput;
	import flash.utils.ByteArray;
	import hbi.wire.ProtocolError;

	public class BinaryChunks implements DataTag
	{
		
		public function get tagValue():*
		{
			return read;
		}
		
		protected function isFinalChunkTag(tag:int):Boolean
		{
			return tag == 'B'.charCodeAt();
		}
		
		protected function isNonFinalChunkTag(tag:int):Boolean
		{
			return tag == 'b'.charCodeAt();
		}
		
		private var isFinalChunkInitial:Boolean;
		
		private var isFinalChunk:Boolean;
		
		public function BinaryChunks(isFinalChunk:Boolean)
		{
			this.isFinalChunk = this.isFinalChunkInitial = isFinalChunk;
		}
		
		public function reset():void
		{
			expectingNextChunkTag = false;
			read = new ByteArray();
			length = -1;
		}
		
		private var expectingNextChunkTag:Boolean = false;
		
		private var read:ByteArray = new ByteArray();
		
		private var length:int = -1;
		
		public function processIncomingData(input:IDataInput):Boolean
		{
			if(length < 0)
			// initial length not read yet
			{
				if(input.bytesAvailable < 2)
				// length not available yet
					return false;
				length = input.readUnsignedShort();
				if(length <= 0)
				{
					if(isFinalChunk)
					{
						read.position = 0;
						return true;
					}
					expectingNextChunkTag = true;
				}
			}
			
			while(input.bytesAvailable > 0)
			{
				if(expectingNextChunkTag)
				{
					// assert read.length == length : "should at chunk boundary here";
					var tag:int = input.readUnsignedByte();
					if(this.isFinalChunkTag(tag))
						this.isFinalChunk = true;
					else if(!this.isNonFinalChunkTag(tag))
						throw new ProtocolError("Unexpected tag: " + tag);
					expectingNextChunkTag = false;
					
					if(input.bytesAvailable < 2) return false;
				}
				
				if(read.length >= length)
				// length of next chunk not read yet
				{
					if(input.bytesAvailable < 2)
					// length not available yet
						return false;
					
					var len:uint = input.readUnsignedShort();
					if(len <= 0)
					{
						if(isFinalChunk)
						{
							read.position = 0;
							return true;
						}
						expectingNextChunkTag = true;
						continue;
					}
					length += len;
					
					if(input.bytesAvailable < 1) return false;
				}
				
				var len2Read:int = Math.min(length-read.length, input.bytesAvailable);
				input.readBytes(read, read.length, len2Read);
				
				if(read.length >= length)
				{
					if(isFinalChunk)
					{
						read.position = 0;
						return true;
					}
					expectingNextChunkTag = true;
				}
			}
			
			return false;
		}
		
	}
}
